home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 10 / AACD 10.iso / AACD / Magazine / Online / httpproxy / src / httpproxy.c < prev    next >
C/C++ Source or Header  |  1996-08-20  |  56KB  |  1,896 lines

  1. /*(( "Header" */
  2. /*
  3.  * $Id: httpproxy.c,v 0.22 1996/08/20 17:36:43 mshopf Exp mshopf $
  4.  *
  5.  * (c) 1995-96 Matthias Hopf
  6.  *
  7.  * A small little Http Proxy.
  8.  * It can serv as a ProxyProxy, too (i.e. it can perform only caching and will
  9.  * get its data from another proxy).
  10.  * That way it can be used for other protocol types than html, too.
  11.  *
  12.  * Run it standalone at high priority. It won't need much computing time as
  13.  * it does no busy wait at all.
  14.  * If you really want to re-get an already cached page, just reload it immedeately.
  15.  * (no other request inbetween and no more than ReloadTime seconds delay).
  16.  * If you browse offline, you'll get a note that the cache is invalid. Reload the
  17.  * page if you want to queue the page and get the old cache.
  18.  */
  19.  
  20. /*
  21.  * $Log: httpproxy.c,v $
  22.  * Revision 0.22  1996/08/20  17:36:43  mshopf
  23.  * new queuing mode.
  24.  * two additional error output messages for the testAmiTCP.rexx script.
  25.  * fixed special handling for graphic Urls.
  26.  * a bit of cleanup.
  27.  * cleaned up template.
  28.  * added minnumreq, noqueue, nohttpproxyproxy option.
  29.  *
  30.  * Revision 0.21  1996/08/12  03:33:36  mshopf
  31.  * new FileToReq().
  32.  * new IsGfx().
  33.  * queued graphic urls dont output 'queued url' message any more
  34.  * but the (invalid) expired cache file or the file @msg/queued.gfx.
  35.  * using extra space in request_t for urls (logging).
  36.  *
  37.  * Revision 0.20  1996/08/11  22:25:15  mshopf
  38.  * reworked debug messages.
  39.  * removed FakeIbrowse switch (finally they made it...).
  40.  * debug switch.
  41.  *
  42.  * Revision 0.19  1996/07/30  13:57:03  mshopf
  43.  * adding shutdown / error messages to bottom of page.
  44.  * keepbad, proxylocal, ibrowse switches added.
  45.  * added queueing.
  46.  * small bug fixes.
  47.  * no url shutdown queueing yet.
  48.  *
  49.  * Revision 0.18  1996/07/17  16:42:42  mshopf
  50.  * support of new cache system.
  51.  *
  52.  * Revision 0.17  1996/06/06  23:01:23  mshopf
  53.  * cosmetic changes (httpproxy support page).
  54.  * added AlwaysReload.
  55.  *
  56.  * Revision 0.16  1996/06/03  04:08:19  mshopf
  57.  * changed strerror handling.
  58.  * added timeouts.
  59.  * url parsing bug fixes.
  60.  * connection close bug fix.
  61.  * shutdown url consistency bug fix.
  62.  *
  63.  * Revision 0.15  1996/04/26  05:14:03  mshopf
  64.  * added ExitAll, service scans.
  65.  * V0.13 alpha 5 fix.
  66.  *
  67.  * Revision 0.14  1996/04/24  17:37:14  mshopf
  68.  * small bug fixes for as225.
  69.  *
  70.  * Revision 0.13  1996/04/24  03:20:13  mshopf
  71.  * encapsulated network module.
  72.  *
  73.  * Revision 0.12  1996/02/12  19:23:36  mshopf
  74.  * assert() to logfile and debugfile and stderr.
  75.  * MaxRequests now variable.
  76.  * ReadCacheList()/SaveCacheList().
  77.  * lots of bug fixes and cosmetic changes.
  78.  *
  79.  * Revision 0.11  1996/01/15  22:27:58  mshopf
  80.  * fixed AmiTCP4.0 broken time() / stat() times (fix by Fionn Behrens).
  81.  * another couple of bug fixes.
  82.  * added dynamic cache table allocation.
  83.  * error replies still getting better.
  84.  * removed host realname lookup.
  85.  *
  86.  * Revision 0.10  1996/01/09  17:20:35  mshopf
  87.  * Three major and some other minor bug fixes.
  88.  * Added UrlBuffer for POST method and non-proxyproxy support.
  89.  * More and more comformant error messages.
  90.  * basic POST support (only proxying, no caching, no queuing).
  91.  * Sending data even when request is not completed yet.
  92.  * Recreating URL even from nonconformant requests.
  93.  * Long requests supported now.
  94.  * Some cosmetic changes.
  95.  *
  96.  * Revision 0.9  1995/12/06  19:53:55  mshopf
  97.  * added fixes for unix machines and AmiTCP4.0 compilation.
  98.  * some bug fixes.
  99.  * more html conform now.
  100.  * lots of printf type fixes.
  101.  *
  102.  * Revision 0.8  1995/12/03  14:20:23  mshopf
  103.  * Made auto requests and proxy messages more http conform.
  104.  *
  105.  * Revision 0.7  1995/11/19  17:57:50  mshopf
  106.  * added revbump compatible version string.
  107.  * fixed ftp offline proxyproxy std port bug.
  108.  *
  109.  * Revision 0.6  1995/11/04  11:26:13  mshopf
  110.  * better shutdown (requeueing of current transmissions). small bug fixes.
  111.  *
  112.  * Revision 0.5  1995/11/02  18:26:13  mshopf
  113.  * queueing system implemented.
  114.  *
  115.  * Revision 0.4  1995/10/21  21:28:37  mshopf
  116.  * small bug fix.
  117.  *
  118.  * Revision 0.3  1995/10/17  19:23:09  mshopf
  119.  * cleaned up messy logging system.
  120.  * new option 'log'.
  121.  *
  122.  * Revision 0.2  1995/10/13  18:09:17  mshopf
  123.  * everything works so far, exceptions are noted in the header.
  124.  * :-)
  125.  *
  126.  * Revision 0.1  1995/10/13  10:44:27  mshopf
  127.  * no caching at all right now, but it works fine as a proxyproxy.
  128.  *
  129.  */
  130.  
  131.  
  132. /*)) */
  133. /*(( "Includes" */
  134.  
  135. #include <stdio.h>
  136. #include <stdlib.h>
  137. #include <stddef.h>
  138. #include <errno.h>
  139. #include <string.h>
  140. #include <unistd.h>
  141. #include <time.h>
  142. #include <ctype.h>
  143.  
  144. #include <dirent.h>
  145. #include <sys/stat.h>
  146. #include <sys/types.h>
  147.  
  148. #include "httpproxy.h"
  149. #include "net.h"
  150. #include "logging.h"
  151. #include "cache.h"
  152. #include "queue.h"
  153. #include "httpproxy_rev.h"
  154.  
  155. #ifdef _AMIGA
  156. #  define strcasecmp stricmp
  157. #  define strncasecmp strnicmp
  158. #  define CURRENTDIR ""
  159.  
  160. #  ifdef FIXTIME
  161. /* Fix broken AmiTCP4.0 (or SasC+AmiTCP4.0) time() / stat() */
  162. #    include <proto/dos.h>
  163.  
  164. time_t time (time_t *TimePtr)
  165. {
  166.     struct DateStamp ds;
  167.     time_t t;
  168.     DateStamp (&ds);
  169.  
  170.     /* Sekunden + Minuten*60 +
  171.      * (Tage + offset zu Unix time()[2922 Tage]) * 60 * 60 * 24  */
  172.     t = ds.ds_Tick / 50+ds.ds_Minute*60+(ds.ds_Days+2922)*86400;
  173.     if (TimePtr)
  174.     *TimePtr = t;
  175.     return (t);
  176. }
  177. #  endif
  178.  
  179. #else
  180. #  define CURRENTDIR "."  /* rudimentary unix support (would need work...) */
  181. #endif
  182.  
  183. /*)) */
  184. /*(( "Global Variables" */
  185.  
  186. #ifdef DEBUG
  187. int DebugLevel = (1<<D_REQUEST) | (1<<D_CHECK) | (1<<D_SCAN) | (1<<D_QUEUE) |
  188.          (1<<D_MSG) | (1<<D_FILES) | (1<<D_ALWAYS);
  189. #endif
  190.  
  191. /* To be kept conform with testAmiTCP.rexx tests */
  192. #define TEST_NOSERVER "Serversocket not supported by installed TCP/IP-Stack."
  193. #define TEST_NOTCPIP  "TCP/IP stack not running."
  194.  
  195. extern long __oslibversion = 37;                    /* Minimum: Kick 2.0 */
  196. request_t    *Requests;
  197. int       OffLine = FALSE;                          /* 0: get cache files again, when they are too old */
  198.                             /* 1: keep cache files and queue them */
  199. int       GetQueued = FALSE;                        /* 1: get queued data from remote hosts or proxyproxy */
  200. int       AlwaysReload = FALSE;                     /* 1: reload *all* requests */
  201. int       QueueMode    = TRUE;                      /* queue requests not yet in the cache */
  202. struct DateStamp RequestStamp;                      /* Current time while request is processed */
  203.  
  204. /* Global variables for httpproxy.c */
  205.  
  206. static    netmethods_t *Net       = NULL;
  207.  
  208. static    char      *VVersion     = VERSTAG;
  209. static    char      *Version      = VERSTRING;
  210. static    char      *PrgName;
  211. #define   TRANSFER_INT "</PRE></TT></TABLE><P><HR>"
  212. static    const char *TShutdown   = TRANSFER_INT "<H1>Shutdown before finished...</H1><HR><ADDRESS>";
  213. static    const char *TReadErr    = TRANSFER_INT "<H1>Read error...</H1><HR><ADDRESS>";
  214. static    const char *TWriteErr   = TRANSFER_INT "<H1>Write error...</H1><HR><ADDRESS>";
  215. static    const char *TTimeout    = TRANSFER_INT "<H1>Timeout...</H1><HR><ADDRESS>";
  216. static    const char *HtmlVersion = "<A HREF=\"http://wwwcip.informatik.uni-erlangen.de/user/mshopf/httpproxy.html\">" VERSTRING
  217.                     "</A> by <A HREF=\"http://wwwcip.informatik.uni-erlangen.de/user/mshopf/\">Matthias Hopf</A>";
  218. static    int       ServerPort    = DEFAULT_PROXYPORT;
  219. static    int       ProxyProxy    = FALSE;                    /* TRUE, when all requests (except Http, see below) should be
  220.                                    * forwarded to another proxy. */
  221. static    int       HttpProxyProxy = FALSE;                   /* TRUE, when HTTP requests should be forwarded, too. */
  222. static    const char *ProxyHost;
  223. static    int       ProxyPort;
  224. struct    DateStamp NextCheckStamp;                           /* wait stamp for next Timeout check */
  225.  
  226. static    char      LastUrl       [MAX_URLBUFFER];
  227. static    time_t    ThisRequestTime = 0;
  228. static    time_t    StartUpTime;
  229. static    int       MaxRequests   = DEFAULT_MAX_REQUESTS;
  230. static    int       MinRequests   = DEFAULT_MIN_REQUESTS;
  231. static    int       RequestsFree  = -1;
  232. static    u_long    DelCacheTime  = DEFAULT_DELTIME;
  233. static    u_long    ExpireCacheTime = DEFAULT_EXPIRETIME;
  234. static    u_long    ReloadCacheTime = DEFAULT_RELOADTIME;
  235. static    int       CacheUnreadRequests = FALSE;              /* 1: keep cache data on data connection close with data in url send buffer */
  236. static    int       KeepUnfinished = FALSE;                   /* 1: keep not finished requests in the cache */
  237. static    int       ProxyLocal    = FALSE;                    /* proxy localhost requests */
  238. static    int       TimeoutTime   = DEFAULT_TIMEOUT;          /* seconds to timeout */
  239.  
  240.  
  241. void DeleteConnect (request_t *Req, int ok);
  242.  
  243. /*)) */
  244.  
  245. /*(( "Init()/ExitAll()" */
  246.  
  247. /* Init all global variables, open server port */
  248.  
  249. void Init (char *ProxyProxyHost, int ProxyProxyPort, char *LogName, char **Argv)
  250. {
  251. #ifdef DEBUG
  252.     char Buffer [1024];          /* Buffer for startup parameters */
  253.     char *Buf;
  254. #endif
  255.  
  256.     ProxyHost = ProxyProxyHost;
  257.     ProxyPort = ProxyProxyPort;
  258.  
  259.     /* Standard setups */
  260.     LogOpenFile (LogName);
  261. #ifdef DEBUG
  262.     assert (*Argv);              /* print out startup arguments */
  263.     Buf = Buffer;
  264.     while (*Argv)
  265.     {
  266.     sprintf (Buf, " \"%s\"", *Argv++);
  267.     Buf += strlen (Buf);
  268.     }
  269.     LogSpecial  ("Startup: %s\n", Buffer);
  270. #endif
  271.  
  272.     if (! (Requests = calloc (MaxRequests, sizeof (request_t))) )
  273.     {
  274.     fprintf (stderr, "not enough memory (need %d Kbyte)\n", NEED_MEM);
  275.     ExitAll (20);
  276.     }
  277.     RequestsFree = MaxRequests;
  278.  
  279.     ThisRequestTime = StartUpTime = time (NULL);  //TODO: remove
  280.  
  281.     DateStamp (&NextCheckStamp);
  282.     RequestStamp = NextCheckStamp;
  283.  
  284.     /* Create Serversocket */
  285.     if (! Net->server (ServerPort))
  286.     {
  287.     fprintf (stderr, "%s\n", Net->strerror (errno));
  288.     fprintf (stdout, TEST_NOSERVER "\n");
  289.     ExitAll (10);
  290.     }
  291.  
  292.     /* Initialize caching system */
  293.     if (! CacheInit ())
  294.     {
  295.     fprintf (stderr, "cache system initialization failed!\n");
  296.     ExitAll (10);
  297.     }
  298.  
  299.     if (! QueueInit ())
  300.     fprintf (stderr, "queue system initialization failed - continuing\n");
  301.  
  302.     if (OffLine)
  303.     LogSpecial ("%s: starting type %s offline\n", Version, Net->Descr);
  304.     else if (ProxyProxyHost)
  305.     LogSpecial ("%s: starting type %s online with proxyproxy host '%s', port %d\n",
  306.             Version, Net->Descr, ProxyProxyHost, ProxyProxyPort);
  307.     else
  308.     LogSpecial ("%s: starting type %s online without proxyproxy\n", Version, Net->Descr);
  309. }
  310.  
  311.  
  312. /* Close network if necessary, exit with return code */
  313.  
  314. void ExitAll (int Ret)
  315. {
  316.     QueueExit ();
  317.     CacheExit ();
  318.     if (Net)
  319.     Net->exit ();
  320.     Net = NULL;
  321.     LogCloseFile ();
  322.     exit (Ret);
  323.     /* NOTREACHED */
  324. }
  325.  
  326. /*)) */
  327. /*(( "ErrToReq()" */
  328.  
  329. /* Type an error to a data buffer of a pending request and set everything up.
  330.  * The error is printed into the Logstream, too. When Errno < 0, a information
  331.  * message is sent (Short is not sent but used for logging when != NULL). */
  332.  
  333. void   ErrToReq (request_t *Req, int Number, int ErrNo, const char *Short, const char *Descr, const char *Url)
  334. {
  335.     Req->DataSent = 0;
  336.  
  337.     if (Req->Flags & REQ_HTTP1X0)
  338.     {
  339.     if (ErrNo >= 0)
  340.     {
  341.         LogErr  (Req, L_ERRMSG, Url, ErrNo, "%s", Short);
  342.         sprintf (Req->DataBuffer, "HTTP/1.0 %03d %s\r\n"
  343.              "Server: %s\r\n"
  344.              "Content-Type: text/html\r\n\r\n"
  345.              "<HTML><HEAD><TITLE>Proxy Error</TITLE></HEAD>\n"
  346.              "<BODY><H1>Proxy Error: %s</H1><P>\n"
  347.              "%s%s%s"
  348.              "%s<P>\n"
  349.              "<HR><ADDRESS>%s, running on %s</ADDRESS>\n"
  350.              "</BODY></HTML>\n",
  351.              Number, Short, VERSHTTP, Short,
  352.              (ErrNo > 0 ? "<H3>Cause: " : ""),
  353.              (ErrNo > 0 ? Net->strerror (ErrNo) : ""),
  354.              (ErrNo > 0 ? "</H3>\n" : ""),
  355.              Descr, HtmlVersion, Net->HostName);
  356.     }
  357.     else
  358.     {
  359.         if (Short)
  360.         LogErr  (Req, L_MSG, Url, 0, "%s", Short);
  361.         sprintf (Req->DataBuffer, "HTTP/1.0 %03d Proxy Message\r\n"
  362.              "Server: %s\r\n"
  363.              "Content-Type: text/html\r\n\r\n"
  364.              "<HTML><HEAD><TITLE>Proxy Message</TITLE></HEAD>\n"
  365.              "<BODY><H3>Proxy Message:</H3><P>\n"
  366.              "%s<P>\n"
  367.              "<HR><ADDRESS>%s, running on %s</ADDRESS>\n"
  368.              "</BODY></HTML>\n",
  369.              Number, VERSHTTP, Descr, HtmlVersion, Net->HostName);
  370.     }
  371.     }
  372.     else
  373.     {
  374.     if (ErrNo >= 0)
  375.     {
  376.         LogErr  (Req, L_ERRMSG, Url, ErrNo, "%s", Short);
  377.         sprintf (Req->DataBuffer, "<HTML><HEAD><TITLE>Proxy Error</TITLE></HEAD>\n"
  378.              "<BODY><H1>Proxy Error: %s</H1><P>\n"
  379.              "%s%s%s"
  380.              "%s<P>\n"
  381.              "<HR><ADDRESS>%s, running on %s</ADDRESS>\n"
  382.              "</BODY></HTML>\n",
  383.              Short,
  384.              (ErrNo > 0 ? "<H3>Cause: " : ""),
  385.              (ErrNo > 0 ? Net->strerror (ErrNo) : ""),
  386.              (ErrNo > 0 ? "</H3>\n" : ""),
  387.              Descr, HtmlVersion, Net->HostName);
  388.     }
  389.     else
  390.     {
  391.         if (Short)
  392.         LogErr  (Req, L_MSG, Url, 0, "%s", Short);
  393.         sprintf (Req->DataBuffer, "<HTML><HEAD><TITLE>Proxy Message</TITLE></HEAD>\n"
  394.              "<BODY><H3>Proxy Message:</H3><P>\n"
  395.              "%s<P>\n"
  396.              "<HR><ADDRESS>%s, running on %s</ADDRESS>\n"
  397.              "</BODY></HTML>\n",
  398.              Descr, HtmlVersion, Net->HostName);
  399.     }
  400.     }
  401.  
  402.     Req->DataRecv = strlen (Req->DataBuffer);
  403.     Req->Flags |= REQ_DONE;
  404.     if (Req->Flags & REQ_CONNSOCKET)
  405.     DeleteConnect (Req, FALSE);
  406. }
  407.  
  408. /*)) */
  409. /*(( "FileToReq() / ErrToData()" */
  410.  
  411. /* Type a file into the DataBuffer.
  412.  * This file has to be short enough to fit in this buffer. */
  413.  
  414. void FileToReq (request_t *Req, const char *File)
  415. {
  416.     FILE *f;
  417.     int max, len;
  418.  
  419.     Req->Flags |= REQ_DONE;
  420.     debug (D_MSG, ("%02d: sending internal file '%s'\n", Req-Requests, File));
  421.     if (! (f = fopen (File, "r")) )
  422.     {
  423.     LogErr  (Req, L_ERRMSG, NULL, errno, "on opening internal file '%s'", File);
  424.     return;
  425.     }
  426.     max = MAX_DATABUFFER - Req->DataRecv;
  427.     if ( (len = fread (& Req->DataBuffer [Req->DataRecv], 1, max, f)) +0 == max)
  428.     LogErr (Req, L_INFO, NULL, 0, "internal file '%s' too long - truncated", File);
  429.     Req->DataRecv += len;
  430.     fclose (f);
  431. }
  432.  
  433.  
  434. /* Type an error to the ending of a data buffer of a working request.
  435.  * The error is not printed into the Logstream. Total maximum size of
  436.  * error messages is RESERVED_DATABUFFER. When KeepCache is true,
  437.  * the error is written to the cache file, too. */
  438.  
  439. void ErrToData (request_t *Req, const char *Err, int KeepCache)
  440. {
  441.     debug (D_MSG, ("%02d: adding error message to request: '%s'\n", Req-Requests, Err));
  442.     if (KeepCache)
  443.     {
  444.     CacheWrite (& Req->Cache, Err, strlen (Err));
  445.     CacheWrite (& Req->Cache, HtmlVersion, strlen (HtmlVersion));
  446.     }
  447.     if (Req->Flags & REQ_REQSOCKET)
  448.     {
  449.     assert (Req->DataRecv + strlen (Err) + strlen (HtmlVersion) < MAX_DATABUFFER);
  450.     strcpy (& Req->DataBuffer [Req->DataRecv], Err);
  451.     Req->DataRecv += strlen (Err);
  452.     strcpy (& Req->DataBuffer [Req->DataRecv], HtmlVersion);
  453.     Req->DataRecv += strlen (HtmlVersion);
  454.     }
  455. }
  456.  
  457. /*)) */
  458. /*(( "CheckCacheTime()" */
  459.  
  460. /* Check the modification time and date of a cache entry; set Time to 0 to force expire. */
  461. /* only to be called when cache file exists. */
  462. /* Returns -1, when the cache entry is expired or to be reloaded.
  463.  * Returns -2, when the cache entry is expired and queued. */
  464.  
  465. int CheckCacheTime (cachefile_t *c, const char *Url, u_long Time)
  466. {
  467.     time_t FileT = -1;
  468.     struct stat s;
  469.  
  470.     // TODO: use cache system
  471.     if (stat (c->FileName, &s) >= 0)
  472.     {
  473.     FileT = s.st_mtime;
  474.     debug (D_CHECK, ("local 0x%lx, access 0x%lx -> %s\n", ThisRequestTime, FileT,
  475.         difftime (ThisRequestTime, FileT) > Time ? "expired" : "valid"));
  476.     if ((! Time) || difftime (ThisRequestTime, FileT) > Time)
  477.     {
  478.         if (OffLine)
  479.         {
  480.         if (QueueMode || (! Time))
  481.         {
  482.             QueueQueue (Url);
  483.             dreturn (D_CHECK, -2);
  484.         }
  485.             /* else: go on */
  486.         }
  487.         else if (QueueCheck (Url))         /* Is the entry queued and not yet sent? */
  488.         dreturn (D_CHECK, OffLine ? -2 : -1);
  489.         else
  490.         FileT = -1;
  491.     }
  492.     }
  493.  
  494.     if (FileT == -1)
  495.     {
  496.     if (QueueCheck (Url))
  497.     {
  498.         debug (D_ALWAYS, ("cache entry '%s' not yet there (queued) - This should not happen...\n", c->File));
  499.         fprintf (stderr, "%s: Warning! Data consistency failure, line %d\n", PrgName, __LINE__);
  500.         dreturn (D_CHECK, -2);
  501.     }
  502.     else
  503.     {
  504.         debug (D_CHECK, ("cache entry '%s' expired / to be reloaded, removing\n", c->File));
  505.         dreturn (D_CHECK, -1);
  506.     }
  507.     }
  508.     dreturn (D_CHECK, (QueueMode && QueueCheck (Url)) ? -2 : 0);
  509. }
  510.  
  511. /*)) */
  512. /*(( "IsGfx()" */
  513.  
  514. /* test whether a specific Url is a graphic */
  515. int IsGfx (const char *Url)
  516. {
  517.     int len = strlen (Url);
  518.     if (strcasecmp (&Url [len-4], ".gif")  == 0 ||
  519.     strcasecmp (&Url [len-4], ".jpg")  == 0 ||
  520.     strcasecmp (&Url [len-5], ".jpeg") == 0 ||
  521.     strcasecmp (&Url [len-4], ".iff")  == 0 ||
  522.     strcasecmp (&Url [len-4], ".png")  == 0 ||
  523.     strcasecmp (&Url [len-4], ".xbm")  == 0 )
  524.     return TRUE;
  525.     return FALSE;
  526. }
  527.  
  528. /*)) */
  529. /*(( "ScanCache()" */
  530.  
  531. /* Scan whether a specific url is already cached, filled and ready to serve */
  532. /* Returns 0: no, not there  1: yes, all set up  -1: Failure
  533.  * 2: error message in buffer */
  534. /* Sets up the request struct for correct filling, opens the cache, etc. */
  535.  
  536. int ScanCache (request_t *Req, char *Url)
  537. {
  538.     static time_t LastRequestTime;
  539.     cachefile_t *c = & Req->Cache;
  540.  
  541.     LastRequestTime = ThisRequestTime;
  542.     ThisRequestTime = time (NULL);
  543.     debug (D_CHECK, ("Url '%s' - reload request: %s\n", Url, difftime (ThisRequestTime, LastRequestTime) < ReloadCacheTime ? "yes" : "no"));
  544.  
  545.     switch (CacheGet (c, Url, Req)) {
  546.  
  547.     case 1:                                              /****** found valid cache file */
  548.  
  549.     switch (CheckCacheTime (c, Url, ExpireCacheTime)) {
  550.     case 0:
  551.         if (AlwaysReload || (strcmp (LastUrl, Url) == 0 && difftime (ThisRequestTime, LastRequestTime) < ReloadCacheTime))
  552.         {
  553.         if (CheckCacheTime (c, Url, 0) == -2)         /* force queueing / reloading */
  554.         {
  555.             ErrToReq (Req, 202, -1, NULL, "Your request for reloading the document is queued.<BR>\n"
  556.                   "You will get the new document next time you are online.<BR>\n"
  557.                   "An expired cache entry exists and can be viewed by immedeately reloading this document.",
  558.                   NULL);
  559.             dreturn (D_CHECK, 2);
  560.         }
  561.         break;                                   /* forcing reload of url */
  562.         }
  563.  
  564.         strcpy (LastUrl, Url);
  565.         if (CacheOpenOld (c) == -1)
  566.         {
  567.         LogErr  (Req, L_WARN, Url, errno, "cannot open cache file '%s', removing", c->FileName);
  568.         CacheRemove (c->FileName, TRUE);
  569.         }
  570.         else
  571.         {
  572.         Req->Flags |= REQ_DONE;
  573.         dreturn (D_CHECK, 1);
  574.         }
  575.         break;
  576.  
  577.         /* case -1: break; */
  578.     case -2:
  579.         if (strcmp (LastUrl, Url) == 0 && difftime (ThisRequestTime, LastRequestTime) < ReloadCacheTime)
  580.         {
  581.         debug (D_CHECK, ("Sending (invalid) cache file '%s' on request\n", c->File));
  582.         if (CacheOpenOld (c) == -1)
  583.         {
  584.             LogErr  (Req, L_WARN, Url, errno, "cannot open cache file '%s', removing", c->FileName);
  585.             CacheRemove (c->FileName, TRUE);
  586.         }
  587.         else
  588.         {
  589.             Req->Flags |= REQ_DONE;
  590.             dreturn (D_CHECK, 1);
  591.         }
  592.         }
  593.         else
  594.         {
  595.         strcpy (LastUrl, Url);
  596.         if (OffLine)
  597.         {
  598.             if (IsGfx (Url))
  599.             {
  600.                 /* don't output up queueing information, but queue request nevertheless */
  601.             debug (D_CHECK, ("Sending (invalid) queued graphics cache file '%s'\n", c->File));
  602.             if (CacheOpenOld (c) == -1)
  603.             {
  604.                 LogErr  (Req, L_WARN, Url, errno, "cannot open cache file '%s', removing", c->FileName);
  605.                 CacheRemove (c->FileName, TRUE);
  606.             }
  607.             else
  608.             {
  609.                 Req->Flags |= REQ_DONE;
  610.                 dreturn (D_CHECK, 1);
  611.             }
  612.             }
  613.             ErrToReq (Req, 202, -1, NULL, "Your request is queued.<BR>\n"
  614.                   "You will get the document next time you are online.<BR>\n"
  615.                   "An expired cache entry exists and can be viewed by immedeately reloading this document.",
  616.                   NULL);
  617.             dreturn (D_CHECK, 2);
  618.         }
  619.         else
  620.             debug (D_CHECK, ("unqueueing and getting Cache entry\n"));
  621.         break;
  622.         }
  623.     }
  624.     break;
  625.  
  626.     case 0:                                              /****** no valid cache file */
  627.  
  628.     if (QueueCheck (Url))
  629.     {
  630.         if (OffLine)
  631.         {
  632.         if (strcmp (LastUrl, Url) == 0 && difftime (ThisRequestTime, LastRequestTime) < ReloadCacheTime)
  633.         {
  634.             ErrToReq (Req, 404, 0, "Already queued", "Your request is already queued.<BR>\n"
  635.                   "You will get the document next time you are online.<BR>\n"
  636.                   "You tried to get an expired cache entry, but this document is not cached right now.",
  637.                   NULL);
  638.             dreturn (D_CHECK, 2);
  639.         }
  640.         else
  641.         {
  642.             strcpy (LastUrl, Url);
  643.             if (IsGfx (Url))
  644.             FileToReq (Req, "@msg/queued.gfx");
  645.             else
  646.             ErrToReq (Req, 202, -1, NULL, "Your request is already queued.<BR>\n"
  647.                   "You will get the document next time you are online.<BR>\n"
  648.                   "There is no expired cache entry for this document.",
  649.                   NULL);
  650.             dreturn (D_CHECK, 2);
  651.         }
  652.         }
  653.         else
  654.         debug (D_CHECK, ("unqueueing and getting Cache entry\n"));
  655.     }
  656.     else
  657.         debug (D_CHECK, ("new cache entry\n"));
  658.     break;
  659.  
  660.     case -1:                                             /****** internal error */
  661.  
  662.     if (OffLine)
  663.     {
  664.         ErrToReq (Req, 500, 0, "Queueing not possible", "Your request can not be queued or served.<BR>\n"
  665.               "While you tried to get an document " PRG_NAME " determined either an internal error or "
  666.               "a doublicate hash entry. These doublicate hash entries can not be cashed.",
  667.               NULL);
  668.         dreturn (D_CHECK, 2);
  669.     }
  670.     dreturn (D_CHECK, 0);                                       /* just proxy it */
  671.  
  672.     }
  673.  
  674.  
  675.     if (OffLine)
  676.     {
  677.     if (QueueMode)
  678.     {
  679.         QueueQueue (Url);
  680.         if (IsGfx (Url))
  681.         FileToReq (Req, "@msg/queued.gfx");
  682.         else
  683.         ErrToReq (Req, 202, -1, NULL, "Your new request is queued.<BR>\n"
  684.               "You will get the document next time you are online.", NULL);
  685.         dreturn (D_CHECK, 2);
  686.     }
  687.     if (IsGfx (Url))
  688.     {
  689.         Req->Flags |= REQ_DONE;
  690.         dreturn (D_CHECK, 2);                          /* send an empty file... */
  691.     }
  692.     ErrToReq (Req, 202, -1, NULL, "No cache available.<BR>\n"
  693.           "There is no cache for the requested document available.<BR>\n"
  694.           "You may queue the document to get it next time you are online by immedeately reloading this document.",
  695.           NULL);
  696.     dreturn (D_CHECK, 2);
  697.     }
  698.  
  699.     if (CacheOpenNew (c, Url) == -1)
  700.     dreturn (D_CHECK, 0);                              /* just proxy it */
  701.  
  702.     Req->Flags |= REQ_NEWCACHE;
  703.     dreturn (D_CHECK, 0);
  704. }
  705.  
  706.  
  707. /*)) */
  708. /*(( "GetCacheData ()" */
  709.  
  710. /* Fill request data space with data from cache
  711.  * when a read error occures, no data will be received and ServWrite()
  712.  * will automagically terminate the socket. */
  713.  
  714. void GetCacheData (request_t *Req)
  715. {
  716.     int Bytes;
  717.  
  718.     assert (Req->DataRecv < MAX_DATABUFFER);           /* No RESERVED_DATABUFFER for error messages is needed */
  719.  
  720.     debug (D_IO, ("Getting more cache data - Url done: %s\n", Req->Flags & REQ_REQDONE ? "yes" : "no"));
  721.  
  722.     if ( (Bytes = CacheRead (& Req->Cache, & Req->DataBuffer [Req->DataRecv],
  723.             MAX_DATABUFFER - Req->DataRecv)) > 0)
  724.     Req->DataRecv += Bytes;
  725.     else
  726.     if (Bytes < 0)
  727.     LogErr  (Req, L_ERROR, NULL, -1, "read error on cache file '%s'", Req->Cache.FileName);
  728.     debug (D_IO, ("Read %d bytes\n", Bytes));
  729. }
  730.  
  731.  
  732. /*)) */
  733. /*(( "BuildFdSets ()" */
  734.  
  735. /* Build fdsets (outstanding reads and writes) */
  736.  
  737. void BuildFdSets (void)
  738. {
  739.     int i;
  740.     request_t *Req;
  741.  
  742.     assert (RequestsFree >= 0);
  743.     debug (D_IO, ("Build: "));
  744.     Net->initfd (RequestsFree > 0);
  745.  
  746.     for (i=0, Req=Requests; i < MaxRequests; i++, Req++)
  747.     {
  748.     if (Req->Flags & REQ_REQSOCKET)
  749.     {
  750.         assert (Req->ReqSocket != 0);
  751.         if (Req->ReqRecv < MAX_REQBUFFER-1)
  752.         {
  753.         debugraw (D_IO, (", Read Req %d", Req-Requests));
  754.         Net->setfdread (Req->ReqSocket);
  755.         }
  756.         if (Req->DataRecv > Req->DataSent)
  757.         {
  758.         debugraw (D_IO, (", Write Req %d", Req-Requests));
  759.         Net->setfdwrite (Req->ReqSocket);
  760.         }
  761.     }
  762.     if (Req->Flags & REQ_CONNSOCKET)
  763.     {
  764.         assert (Req->ConnSocket != 0);
  765.         if (Req->ReqRecv > Req->ReqSent || Req->UrlRecv > Req->UrlSent) /* Waiting for connect or sending request */
  766.         {
  767.         debugraw (D_IO, (", Write Con %d", Req-Requests));
  768.         Net->setfdwrite (Req->ConnSocket);
  769.         }
  770.         if (Req->DataRecv < MAX_DATABUFFER - RESERVED_DATABUFFER)       /* Transfer Data */
  771.         {
  772.         debugraw (D_IO, (", Read Con %d", Req-Requests));
  773.         Net->setfdread (Req->ConnSocket);
  774.         }
  775.     }
  776.     }
  777.     debugraw (D_IO, ("\n"));
  778. }
  779.  
  780.  
  781. /*)) */
  782. /*(( "CreateUrlRequest()/CheckUrl()" */
  783.  
  784. /* Create Url request according to specification */
  785. /* Returns the length of the request. */
  786. /* Prot and Host may be NULL, in that case Name is to be asumed to be a full URL.
  787.  * No checks for Proxyproxy mode are done in this case. */
  788.  
  789. size_t CreateUrlRequest (const char *Method, const char *Prot, const char *Host, int Port, const char *Name, char *ReqSpace, int HttpOne, int ExpandProcent)
  790. {
  791.     char Buffer [MAX_URLBUFFER];
  792.     char *p, *DokPrint;
  793.  
  794.     if (Prot && Host)
  795.     {
  796.     if ( (strcmp (Prot, "http") == 0) ? HttpProxyProxy : ProxyProxy)
  797.     {
  798.         if (Port > -1)
  799.         sprintf (ReqSpace, "%s %s://%s:%d/", Method, Prot, Host, Port);
  800.         else
  801.         sprintf (ReqSpace, "%s %s://%s/", Method, Prot, Host);
  802.     }
  803.     else
  804.         sprintf (ReqSpace, "%s /", Method);
  805.     }
  806.     else
  807.     sprintf (ReqSpace, "%s ", Method);
  808.  
  809.     DokPrint = ReqSpace + strlen (ReqSpace);
  810.  
  811.     for (p = Buffer; *Name; )
  812.     if (isvalidhttp (*Name) || (*Name == '%' && ! ExpandProcent))
  813.         *p++ = *Name++;
  814.     else
  815.     {
  816.         sprintf (p, "%%%02x", *Name++);
  817.         p += 3;
  818.     }
  819.     *p = '\0';
  820.  
  821.     debug (D_CHECK, ("Request '%s' created - HTTP1.0:%s\n", ReqSpace, HttpOne ? "yes" : "no"));
  822.     if (HttpOne)
  823.     sprintf (DokPrint, "%s HTTP/1.0\r\n", Buffer);
  824.     else
  825.     sprintf (DokPrint, "%s\r\n", Buffer);
  826.     return (strlen (ReqSpace));
  827. }
  828.  
  829.  
  830. /* Check whether the URL is read completely. Sets REQ_REQDONE accordingly.
  831.  * Attention! This routine relys on the fact, that ScanUrl was called already!
  832.  * That means especially, that the first line was already read completely. */
  833. /* The routine does not check for Url termination on Buffer boundaries. So
  834.  * the ReqBuffer has to be shifted always (four bytes remaining always) and
  835.  * never be cleared at all. */
  836.  
  837. void CheckUrl (request_t *Req)
  838. {
  839.     debug (D_IO, ("Checking Url\n"));
  840.     if (Req->Flags & REQ_HTTP1X0)
  841.     {
  842.     if (strstr (Req->ReqBuffer, "\n\n") || strstr (Req->ReqBuffer, "\r\r") ||
  843.         strstr (Req->ReqBuffer, "\n\r\n\r") || strstr (Req->ReqBuffer, "\r\n\r\n"))
  844.         Req->Flags |= REQ_REQDONE;
  845.     }
  846.     else
  847.     Req->Flags |= REQ_REQDONE;
  848. #ifdef DEBUG
  849.    if (Req->Flags & REQ_REQDONE)
  850.     debug (D_IO, ("http request complete\n"));
  851. #endif
  852. }
  853.  
  854.  
  855. /*)) */
  856. /*(( "ScanUrl ()" */
  857.  
  858. /* Scan the URL and divide it into several parts. Understands http/0.9
  859.  * and http/1.0 versions right now. Sets REQ_REQDONE when URL is complete. */
  860. /* Req->UrlBuffer is valid after calling this routine and REQ_URLDONE set. */
  861. /* Returns -1 in case of failure, 0 on correct termination or found cache,
  862.  * 1: found cache slot, FileD is open, 2: failure, cache contains error. */
  863. /* Port and Address are value-return arguments. */
  864.  
  865. int ScanUrl (request_t *Req, char *Address, int *Port, const char **SaveUrl, const char **SaveProt)
  866. {
  867.     static char BufDOK [MAX_URLBUFFER +256];                                       /* a critical array... but that's enough */
  868.     static char BufPROT [16];
  869.  
  870.     char BufMETH [12];
  871.     char BufURL [1024];
  872.     char BufVERS [8];
  873.     int  CharsRead, CharsRead2, i;
  874.     char *DokPtr, *PortPtr;
  875.     char *Host, *ObjectName;
  876.  
  877.     if (sscanf (Req->ReqBuffer, "%11s %15[a-zA-Z0-9]%n", BufMETH, BufPROT, &CharsRead) < 2)
  878.     return (-1);
  879.     debug (D_SCAN, ("METHOD '%s', PROT '%s'\n", BufMETH, BufPROT));
  880.     if (CharsRead >= Req->ReqRecv)
  881.     return (-1);
  882.     if (strncmp (& Req->ReqBuffer [CharsRead], "://", 3) != 0)
  883.     return (-1);
  884.     if (sscanf (& Req->ReqBuffer [CharsRead+3], "%1022s%n", BufURL, &CharsRead2) < 1)
  885.     return (-1);
  886.     debug (D_SCAN, ("URL '%s'\n", BufURL));
  887.     CharsRead += 3 + CharsRead2;
  888.  
  889.     if (! (DokPtr = strchr (BufURL, '/')) )
  890.     DokPtr = BufURL + strlen (BufURL);                                 /* empty Objectname (Root) */
  891.     else
  892.     *DokPtr++ = 0;
  893.  
  894.     if (strcasecmp (BufPROT, "http") != 0)
  895.     *Port = -1;
  896.     else
  897.     *Port = Net->StdHttpPort;
  898.     if ( (PortPtr = strchr (BufURL, ':')) )
  899.     {
  900.     *PortPtr = 0;
  901.     *Port = atoi (PortPtr + 1);
  902.     }
  903.  
  904.     Host = BufURL;
  905.     strncpy (Address, BufURL, MAX_HOSTNAMELEN-1);
  906.     Address [MAX_HOSTNAMELEN-1] = '\0';
  907.  
  908.     BufVERS [2] = 0;                                                       /* BufVERS is missused here... */
  909.     if (*Port > -1)
  910.     sprintf (BufDOK, "%s://%s:%d/", BufPROT, Host, *Port);
  911.     else
  912.     sprintf (BufDOK, "%s://%s/", BufPROT, Host);
  913.     ObjectName = BufDOK + strlen (BufDOK);
  914.     for (PortPtr = ObjectName; *DokPtr; DokPtr++, PortPtr++)
  915.     {
  916.     if (*DokPtr == '%')
  917.     {
  918.         if (! (BufVERS [0] = *++DokPtr) )
  919.         break;
  920.         if (! (BufVERS [1] = *++DokPtr) )
  921.         break;
  922.         if ( (i = strtol (BufVERS, NULL, 0x10)) )
  923.         *PortPtr = i;
  924.         else
  925.         *PortPtr = '?';
  926.     }
  927.     else
  928.         *PortPtr = *DokPtr;
  929.     }
  930.     *PortPtr = 0;
  931.  
  932.     *SaveUrl = BufDOK;
  933.     *SaveProt = BufPROT;
  934.     debug (D_SCAN, ("Proto '%s', Host '%s', Port %d, Dok '%s'\n", BufPROT, Host, *Port, BufDOK));
  935.  
  936.     if (OffLine && strcasecmp (BufMETH, "get") != 0)                       /* no get command */
  937.     return (-1);
  938.     if (! ProxyProxy)
  939.     if (strcasecmp (BufPROT, "http") != 0)                             /* we only support http urls so far */
  940.         return (-1);
  941.  
  942.     if (Req->ReqBuffer [CharsRead] == ' ' && CharsRead < Req->ReqRecv)
  943.     if (sscanf (& Req->ReqBuffer [CharsRead], " %5s", BufVERS) == 1)
  944.         if (strcasecmp (BufVERS, "http/") == 0)
  945.         {
  946.         debug (D_SCAN, ("got HTTP/1.0 or greater request\n"));
  947.         Req->Flags |= REQ_HTTP1X0;
  948.         }
  949.  
  950.     CheckUrl (Req);
  951.     Req->Flags  |= REQ_URLDONE;
  952.     strncpy (Req->Url, BufDOK, MAX_URLSAVE-1);
  953.     Req->Url [MAX_URLSAVE-1] = '\0';
  954.  
  955.     if (strlen (BufDOK) > MAX_URLSAVE-1)                     /* That one cannot be cached */
  956.     {
  957.     LogErr  (Req, L_INFO, BufDOK, 0, "ReqBuffer size (%d chars) exceeded - document is not cached", MAX_URLSAVE-1);
  958.     strncpy (Req->Url, BufDOK, MAX_URLSAVE-1);
  959.     Req->Url [MAX_URLSAVE-1] = '\0';
  960.     return (0);
  961.     }
  962.  
  963.     /* check for service URLs */
  964.     if (strcasecmp (Host, "proxy...") == 0)
  965.     {
  966.     if (strcasecmp (Req->Address, "localhost") == 0 || strcmp (Req->Address, "127.0.0.1") == 0)
  967.         return (ScanService (Req, ObjectName));
  968.     else
  969.     {
  970.         ErrToReq (Req, 403, -1, "Service URLs are not allowed", "You are not allowed to request service URLs"
  971.               "from other hosts than the one httpproxy is started on.",
  972.               BufDOK);
  973.         LogErr  (Req, L_INFO, BufDOK, 0, "Service URL tried from '%s'", Req->Address);
  974.         return (2);
  975.     }
  976.     }
  977.  
  978.     /* create UrlBuffer entry */
  979.     assert (Req->UrlSent == 0);
  980.     Req->UrlRecv = CreateUrlRequest (BufMETH, BufPROT, Host, *Port, ObjectName, Req->UrlBuffer, Req->Flags & REQ_HTTP1X0, FALSE);
  981.     PortPtr = strchr (& Req->ReqBuffer [CharsRead], '\n');   /* PortPtr und DokPtr are missused here */
  982.     DokPtr  = strchr (& Req->ReqBuffer [CharsRead], '\r');
  983.     assert (DokPtr == NULL || PortPtr == NULL || PortPtr == DokPtr + 1 || DokPtr == PortPtr + 1);
  984.     if (DokPtr == PortPtr + 1 || PortPtr == NULL)
  985.     PortPtr = DokPtr;
  986.     assert (PortPtr != NULL);
  987.  
  988.     Req->ReqSent = PortPtr - Req->ReqBuffer + 1;
  989.  
  990.     if (ProxyLocal)
  991.     if (strcasecmp (Host, "localhost") == 0 || strncasecmp (Host, Net->HostName, strlen (Host)) == 0)
  992.         return (0);                                                    /* proxy localhost requests */
  993.  
  994.     if (strcasecmp (BufMETH, "get") != 0)                                  /* only get's will be cached */
  995.     {
  996.     debug (D_ALWAYS, ("no get method - no caching\n"));
  997.     return (0);
  998.     }
  999.  
  1000.     return (ScanCache (Req, BufDOK));
  1001. }
  1002.  
  1003.  
  1004. /*)) */
  1005. /*(( "DeleteRequest/Connect ()" */
  1006.  
  1007. /* Close the connection socket only. When a request socket is still open
  1008.  * and no data is in the cache, close all.
  1009.  * Delete cache entry in case of an error. Check the answer whether it is
  1010.  * an error message or a regular answer. Errors should not be cached! */
  1011. /* When Flags == 0 nothing is done at all */
  1012.  
  1013. void DeleteConnect (request_t *Req, int ok)
  1014. {
  1015.     int CacheOk;
  1016.  
  1017.     CacheOk = (Req->Flags & REQ_NEWCACHE) ? ok : TRUE;
  1018.  
  1019.     debug (D_REQUEST, ("%02d: DeleteCon Flags 0x%x (ok:%s cacheok:%s)\n", Req-Requests, Req->Flags, ok ? "yes" : "no", CacheOk ? "yes" : "no"));
  1020.     if (! Req->Flags)
  1021.     return;
  1022.  
  1023.     if (Req->Flags & (REQ_CONNSOCKET | REQ_CONNERR))
  1024.     {
  1025.     if (Req->Flags & REQ_CONNSOCKET)
  1026.         Net->close (Req->ConnSocket);
  1027.     Req->Flags &= ~(REQ_CONNSOCKET | REQ_CONNERR);
  1028.     if (ok)
  1029.     {
  1030.         if (Req->Flags & REQ_NEWCACHE)
  1031.         LogStd (Req, "new");
  1032.         else
  1033.         LogStd (Req, "proxied");
  1034.     }
  1035.     }
  1036.     LogStd (Req, NULL);                                 /* There really *should* be a log entry */
  1037.     if (Req->Flags & REQ_REQSOCKET)
  1038.     {
  1039.     if (Req->DataRecv <= Req->DataSent && (Req->Flags & REQ_REQDONE))
  1040.     {
  1041.         Net->close (Req->ReqSocket);       /* All done. Hugh! */
  1042.         Req->Flags = 0;
  1043.     }
  1044.     else
  1045.         Req->Flags |= REQ_DONE;
  1046.     }
  1047.     else
  1048.     Req->Flags = 0;
  1049.  
  1050.     CacheClose (& Req->Cache, CacheOk);
  1051.     if (! (Req->Flags & REQ_PENDINGMASK))
  1052.     {
  1053.     Req->Flags = 0;
  1054.     RequestsFree++;
  1055.     }
  1056. }
  1057.  
  1058.  
  1059. /* Close the request socket only. When a connection socket is already up
  1060.  * (and enough data is already read) continue filling up the cache. When
  1061.  * everything is done, close all. The URL should be checked, too.
  1062.  * When it is not complete, all connections should be terminated
  1063.  * on CacheUnreadRequests false. */
  1064. /* Don't call DeleteConnect when Flags == 0... */
  1065.  
  1066. void DeleteRequest (request_t *Req, int ok)
  1067. {
  1068.     int CacheOk;
  1069.  
  1070.     CacheOk = (Req->Flags & REQ_NEWCACHE) ? ok : TRUE;
  1071.  
  1072.     debug (D_REQUEST, ("%02d: DeleteReq Flags 0x%x (ok:%s cacheok:%s)\n", Req-Requests, Req->Flags, ok ? "yes" : "no", CacheOk ? "yes" : "no"));
  1073.     if (Req->Flags & REQ_REQSOCKET)
  1074.     {
  1075.     Net->close (Req->ReqSocket);
  1076.     Req->Flags &= ~REQ_REQSOCKET;
  1077.     }
  1078.  
  1079.     if (Req->Flags & REQ_DONE)                  /* Maybe we're getting data from the cache */
  1080.     {
  1081.     if (QueueCheck (Req->Url))
  1082.         LogStd (Req, "queued");
  1083.     else
  1084.         LogStd (Req, "cached");
  1085.     CacheClose (& Req->Cache, CacheOk);
  1086.     DeleteConnect (Req, ok);
  1087.     debug (D_REQUEST, ("All done - deleterequest\n"));
  1088.     }
  1089.     else if (Req->Flags & REQ_REQDONE)
  1090.     {
  1091.     if (! (Req->Flags & REQ_CONNSOCKET))    /* Aborting cache sending */
  1092.         DeleteConnect (Req, FALSE);
  1093.     else if (CacheUnreadRequests)
  1094.         Req->DataSent = Req->DataRecv = 0;  /* Continue getting data into the cache */
  1095.     else
  1096.         DeleteConnect (Req, FALSE);         /* Remove connection */
  1097.     }
  1098.     else
  1099.     {
  1100.     Req->Flags |= REQ_DONE;
  1101.     DeleteConnect (Req, FALSE);             /* No request known so far... */
  1102.     CacheClose (& Req->Cache, CacheOk);
  1103.     }
  1104. }
  1105.  
  1106.  
  1107. /*)) */
  1108. /*(( "ServConnect ()" */
  1109.  
  1110. /* The first line of the URL is already there, so we can connect to the
  1111.  * remote host. */
  1112.  
  1113. void ServConnect (request_t *Req)
  1114. {
  1115.     char Host [MAX_HOSTNAMELEN];
  1116.     int  Port, UseProxy = FALSE;
  1117.     const char *Url   = NULL;             /* only for logging */
  1118.     const char *Proto = NULL;             /* only for proxyproxy check */
  1119.  
  1120.     debug (D_REQUEST, ("%02d: ServCon for Req; Flags 0x%x\n", Req-Requests, Req->Flags));
  1121.  
  1122.     if (Req->Flags & REQ_REQSOCKET)
  1123.     {
  1124.     if (strchr (Req->ReqBuffer, '\n') == 0 &&
  1125.         strchr (Req->ReqBuffer, '\r') == 0)         /* first line of URL is not there... */
  1126.         return;
  1127.  
  1128.     if (Req->Flags & REQ_URLDONE)  /* already set up */
  1129.     {
  1130.         CheckUrl (Req);
  1131.         return;
  1132.     }
  1133.     }
  1134.  
  1135.     switch (ScanUrl (Req, Host, &Port, &Url, &Proto)) {
  1136.     case 0:                                   /* need to get document from remote host */
  1137. #ifdef DEBUG
  1138.     if (! (Req->Flags & REQ_NEWCACHE))
  1139.         debug  (D_REQUEST, ("%02d: serving uncacheable request '%s'\n",
  1140.                 Req-Requests, Req->ReqBuffer));
  1141. #endif
  1142.     break;
  1143.  
  1144.     case 1:                                   /* already cached - no need to connect to remote host */
  1145.     GetCacheData (Req);
  1146.     return;
  1147.  
  1148.     case 2:                                   /* send error message from buffer */
  1149.     return;
  1150.  
  1151.     default:
  1152.     {
  1153.         Req->Flags |= REQ_DONE | REQ_REQDONE;
  1154.         ErrToReq (Req, 400, 0, "Invalid Request", "Your Request is not a valid URL.<BR>\n"
  1155.               "Please check it and try again. Perhaps you tried to queue a page not using the 'GET' method\n"
  1156.               "or you tried to get a non-HTTP: URL without proxyproxy.",
  1157.               Url);
  1158.         return;
  1159.     }
  1160.     }
  1161.  
  1162.     UseProxy = (strcmp (Proto, "http") == 0) ? HttpProxyProxy : ProxyProxy;
  1163.     if (OffLine)
  1164.     {
  1165.     ErrToReq (Req, 500, 0, "Unable to serv", "It is not possible to connect to a remote host in offline mode.<BR>\n"
  1166.           "Either the requested URL is to long to be queued or an internal error has occured. When the\n"
  1167.           "URL is rather short, please contact the author of " PRG_NAME,
  1168.           Url);
  1169.     return;
  1170.     }
  1171.  
  1172.     if (UseProxy)
  1173.     {
  1174.     if ( (Req->ConnSocket = Net->open (ProxyHost, ProxyPort)) < 0)
  1175.     {
  1176.         Req->Flags |= REQ_CONNERR;
  1177.         ErrToReq (Req, 404, errno, "Proxyproxy Host Unreachable / read() failed",
  1178.               "The host you specified with the 'proxy' option can't be contacted.<BR>\n"
  1179.               "Please wait until the proxy is up again or start " PRG_NAME " in offline mode.",
  1180.               NULL);
  1181.         return;
  1182.     }
  1183.     }
  1184.     else
  1185.     {
  1186.     if ( (Req->ConnSocket = Net->open (Host, Port)) < 0)
  1187.     {
  1188.         Req->Flags |= REQ_CONNERR;
  1189.         ErrToReq (Req, 404, errno, "Host Unreachable / read() failed",
  1190.               "The specified host can't be contacted.<BR>\n"
  1191.               "Please wait until it is up again or try any other URLs.",
  1192.               NULL);
  1193.         return;
  1194.     }
  1195.     }
  1196.  
  1197.     Req->Flags |= REQ_CONNSOCKET;
  1198.  
  1199.     debug (D_REQUEST, ("ServCon -> Conn %d\n", (int) Req->ConnSocket));
  1200. }
  1201.  
  1202.  
  1203. /*)) */
  1204. /*(( "ServServer ()" */
  1205.  
  1206. /* Serv the server port. Accept new connections, set up request database */
  1207.  
  1208. void ServServer (int Peer, const char *PeerName)
  1209. {
  1210.     request_t *Req = Requests;
  1211.  
  1212.     assert (RequestsFree > 0);
  1213.     DateStamp (&RequestStamp);
  1214.  
  1215.     while (Req->Flags)                        /* any Flags set -> occupied */
  1216.     Req++;
  1217.     assert (Req - Requests < MaxRequests);
  1218.  
  1219.     strncpy (Req->Address, PeerName, MAX_HOSTNAMELEN-1);
  1220.     Req->Address [MAX_HOSTNAMELEN-1] = '\0';
  1221.     debug  (D_REQUEST, ("%02d: new request from '%s'\n", Req-Requests, PeerName));
  1222.  
  1223.     Req->ReqSocket = Peer;
  1224.     Req->Flags    |= REQ_REQSOCKET;
  1225.     Req->ReqBuffer[0] = Req->UrlBuffer [0] = Req->DataBuffer [0] = Req->Url [0] = '\0';
  1226.     Req->ReqSent   = Req->ReqRecv = Req->UrlSent = Req->UrlRecv =
  1227.              Req->DataSent = Req->DataRecv = 0;
  1228.     Req->LastStamp = RequestStamp;
  1229.     RequestsFree--;
  1230. }
  1231.  
  1232.  
  1233. /*)) */
  1234. /*(( "ServRead ()" */
  1235.  
  1236. /* Server for all reads */
  1237.  
  1238. void ServRead (void)
  1239. {
  1240.     int i, Bytes;
  1241.     register request_t *Req;
  1242.  
  1243.     for (i=0, Req=Requests; i < MaxRequests; i++, Req++)
  1244.     {
  1245.     if ((Req->Flags & REQ_REQSOCKET) && Net->checkread (Req->ReqSocket))
  1246.     {
  1247.         assert (Req->ReqRecv < MAX_REQBUFFER-1);
  1248.         if ( (Bytes = Net->read (Req->ReqSocket, & Req->ReqBuffer [Req->ReqRecv],
  1249.                      MAX_REQBUFFER-1 - Req->ReqRecv)) < 0)
  1250.         {
  1251.         LogErr  (Req, L_ERROR, NULL, errno, "on receiving request");
  1252.         DeleteRequest (Req, FALSE);
  1253.         }
  1254.         else
  1255.         {
  1256.         Req->LastStamp = RequestStamp;
  1257.         Req->ReqRecv += Bytes;
  1258.         Req->ReqBuffer [Req->ReqRecv] = '\0';        /* needed for strchr() (and debug) */
  1259.  
  1260.         debug (D_IO, ("%02d: Read %d bytes to 0x%x containing:\n'%s'\n", Req-Requests, Bytes,
  1261.                   Req->ReqRecv-Bytes, & Req->ReqBuffer [Req->ReqRecv - Bytes]));
  1262. #if defined (DEBUG) && ! defined (_M68020)
  1263.         if ((Req->ReqRecv - Bytes) & 0x0001)
  1264.             debug (D_IO, ("(odd address...\n"));
  1265.         else
  1266. #endif
  1267.         debug (D_IO, ("= 0x %08lx %08lx %08lx %08lx\n", * (long *) (Req->ReqBuffer + Req->ReqRecv - Bytes),
  1268.                   * (long *) (Req->ReqBuffer + Req->ReqRecv - Bytes + 4), * (long *) (Req->ReqBuffer + Req->ReqRecv - Bytes + 8),
  1269.                   * (long *) (Req->ReqBuffer + Req->ReqRecv - Bytes + 12)));
  1270.  
  1271.         if (Bytes == 0)                              /* request socket gone away */
  1272.         {
  1273.             DeleteRequest (Req, TRUE);
  1274.             continue;
  1275.         }
  1276.         ServConnect (Req);                           /* Check if anything can be done already */
  1277.         if ((Req->Flags & REQ_DONE) && (Req->Flags & REQ_REQDONE) && (Req->DataRecv == 0))
  1278.         {
  1279.             DeleteRequest (Req, TRUE);               /* all done, another time */
  1280.             continue;
  1281.         }
  1282.         if ((Req->ReqRecv == MAX_REQBUFFER-1) && ! (Req->Flags & REQ_CONNSOCKET))
  1283.         {                                            /* buffer full and no connection yet */
  1284.             if (Req->Flags & REQ_URLDONE)
  1285.             Req->ReqRecv = Req->ReqSent = 0;     /* Skip remaining data (we won't send it...) */
  1286.             else
  1287.             {
  1288.             LogErr  (Req, L_ERROR, NULL, 0, "URL buffer (size %d) overflow", MAX_REQBUFFER-1);
  1289.             DeleteRequest (Req, FALSE);
  1290.             }
  1291.         }
  1292.         }
  1293.     }
  1294.     if ((Req->Flags & REQ_CONNSOCKET) && Net->checkread (Req->ConnSocket))
  1295.     {
  1296.         assert (Req->DataRecv < MAX_DATABUFFER - RESERVED_DATABUFFER);
  1297.         if ( (Bytes = Net->read (Req->ConnSocket, & Req->DataBuffer [Req->DataRecv],
  1298.                      MAX_DATABUFFER - RESERVED_DATABUFFER - Req->DataRecv)) < 0)
  1299.         {
  1300.         ErrToReq (Req, 404, errno, "Host Unreachable / read() failed",
  1301.               "The specified host can't be contacted.<BR>\n"
  1302.               "Please wait until it is up again or try any other URLs.",
  1303.               NULL);
  1304.             // above only for 0 bytes read.
  1305.             // ErrToData (Req, TReadErr, KeepUnfinished);
  1306.         DeleteConnect (Req, KeepUnfinished);
  1307.         }
  1308.         else
  1309.         {
  1310.         Req->LastStamp = RequestStamp;
  1311.         debug (D_IO, ("%02d: Read %d bytes from Con %d\n", Req-Requests, Bytes));
  1312.         if (Bytes == 0)                               /* We're done */
  1313.         {
  1314.             DeleteConnect (Req, TRUE);
  1315.             continue;
  1316.         }
  1317.         else if (Req->Flags & REQ_NEWCACHE)
  1318.             CacheWrite (&Req->Cache, & Req->DataBuffer [Req->DataRecv], Bytes);      /* no error checking... */
  1319.         if (Req->Flags & REQ_REQSOCKET)
  1320.             Req->DataRecv += Bytes;                   /* else: only save cache data */
  1321.         }
  1322.     }
  1323.     }
  1324. }
  1325.  
  1326.  
  1327. /*)) */
  1328. /*(( "ServWrite ()" */
  1329.  
  1330. /* Server for all writes */
  1331.  
  1332. void ServWrite (void)
  1333. {
  1334.     int i, Bytes;
  1335.     register request_t *Req;
  1336.  
  1337.     for (i=0, Req=Requests; i < MaxRequests; i++, Req++)
  1338.     {
  1339.     if ((Req->Flags & REQ_REQSOCKET) && Net->checkwrite (Req->ReqSocket))
  1340.     {
  1341.         assert (Req->DataRecv > Req->DataSent);
  1342.         if ( (Bytes = Net->write (Req->ReqSocket, & Req->DataBuffer [Req->DataSent],
  1343.                       Req->DataRecv - Req->DataSent)) < 0)
  1344.         {
  1345.         LogErr  (Req, L_ERROR, NULL, errno, "on sending data");
  1346.         DeleteRequest (Req, FALSE);
  1347.         continue;
  1348.         }
  1349.         else
  1350.         {
  1351.         Req->LastStamp = RequestStamp;
  1352.         debug (D_IO, ("%02d: Wrote %d bytes to Req\n", Req-Requests, Bytes));
  1353.         Req->DataSent += Bytes;
  1354.         assert (Req->DataRecv >= Req->DataSent);
  1355.         if (Bytes == 0)                            /* request socket is unable to receive data */
  1356.         {
  1357.             LogErr  (Req, L_ERROR, NULL, 0, "request socket unable to receive data");
  1358.             DeleteRequest (Req, FALSE);
  1359.             continue;
  1360.         }
  1361.  
  1362.         if (Req->DataSent == Req->DataRecv)        /* clear / shift data buffer */
  1363.             Req->DataSent = Req->DataRecv = 0;
  1364.         else if (Req->DataSent > SHIFT_DATABUFFER)
  1365.         {
  1366.             debug (D_IO, ("shifting databuffer size %d by %d bytes\n", Req->DataRecv - Req->DataSent, Req->DataSent));
  1367.             memmove (Req->DataBuffer, & Req->DataBuffer [Req->DataSent],
  1368.                  Req->DataRecv - Req->DataSent);
  1369.             Req->DataRecv -= Req->DataSent;
  1370.             Req->DataSent = 0;
  1371.         }
  1372.         if (Req->DataRecv < MAX_DATABUFFER && (Req->Flags & REQ_DONE) && ! (Req->Flags & REQ_CONNSOCKET))
  1373.             GetCacheData (Req);
  1374.         if (Req->DataRecv == 0 && (Req->Flags & REQ_DONE) && (Req->Flags & REQ_REQDONE))   /* We're already done */
  1375.             DeleteRequest (Req, TRUE);
  1376.  
  1377.         }
  1378.     }
  1379.     if ((Req->Flags & REQ_CONNSOCKET) && Net->checkwrite (Req->ConnSocket))
  1380.     {
  1381.         if (Req->UrlRecv > Req->UrlSent)
  1382.         Bytes = Net->write (Req->ConnSocket, &Req->UrlBuffer [Req->UrlSent],
  1383.                     Req->UrlRecv - Req->UrlSent);
  1384.         else
  1385.         {
  1386.         assert (Req->ReqRecv > Req->ReqSent);
  1387.         Bytes = Net->write (Req->ConnSocket, & Req->ReqBuffer [Req->ReqSent],
  1388.                     Req->ReqRecv - Req->ReqSent);
  1389.         }
  1390.         if (Bytes < 0)
  1391.         {
  1392.         LogErr  (Req, L_ERROR, NULL, errno, "send error");
  1393.         ErrToData (Req, TWriteErr, FALSE);
  1394.         DeleteConnect (Req, FALSE);
  1395.         }
  1396.         else
  1397.         {
  1398.         Req->LastStamp = RequestStamp;
  1399.         debug (D_IO, ("%02d: Wrote %d bytes to Conn from %s, 0x%x\n", Req-Requests, Bytes,
  1400.             Req->UrlRecv > Req->UrlSent ? "UrlBuffer" : "ReqBuffer", Req->UrlRecv > Req->UrlSent ? Req->UrlSent : Req->ReqSent));
  1401.  
  1402.         if (Req->UrlRecv > Req->UrlSent)
  1403.             Req->UrlSent += Bytes;
  1404.         else
  1405.             Req->ReqSent += Bytes;
  1406.         if (Bytes == 0)                               /* connection socket is unable to receive url */
  1407.         {
  1408.             LogErr  (Req, L_ERROR, NULL, errno, "connection socket unable to receive request");
  1409.             ErrToData (Req, TWriteErr, FALSE);
  1410.             DeleteConnect (Req, FALSE);
  1411.             continue;
  1412.         }
  1413.  
  1414.         if (Req->ReqSent > SHIFT_REQBUFFER)
  1415.         {
  1416.             debug (D_IO, ("shifting reqbuffer size %d+4 by %d bytes\n", Req->ReqRecv - Req->ReqSent, Req->ReqSent-4));
  1417.             memmove (Req->ReqBuffer, & Req->ReqBuffer [Req->ReqSent - 4],
  1418.                  Req->ReqRecv - Req->ReqSent + 4);
  1419.             Req->ReqRecv -= Req->ReqSent - 4;
  1420.             Req->ReqSent = 4;
  1421.         }
  1422.         }
  1423.     }
  1424.     }
  1425. }
  1426.  
  1427.  
  1428. /*)) */
  1429. /*(( "RequestQueued()" */
  1430.  
  1431. /* Initiate contact to remote host for a queued request */
  1432.  
  1433. void RequestQueued (const char *Url)
  1434. {
  1435.     request_t *Req = Requests;
  1436.     int     Len;
  1437.  
  1438.     assert (MAX_REQBUFFER >= 4 * MAX_URLSAVE);  /* to be on the save side */
  1439.  
  1440.     while (Req->Flags)                        /* any Flags set -> occupied */
  1441.     Req++;
  1442.     assert (Req - Requests < MaxRequests);
  1443.  
  1444.     Req->Flags  = REQ_REQDONE;
  1445.     Req->ReqSent= Req->DataSent = Req->DataRecv = Req->UrlSent = Req->UrlRecv = 0;
  1446.     Req->Address[0] = Req->Url[0] = '\0';
  1447.     Req->LastStamp = RequestStamp;
  1448.     RequestsFree--;
  1449.  
  1450.     Len = CreateUrlRequest ("GET", NULL, NULL, 0, Url, Req->ReqBuffer, TRUE, TRUE);
  1451.     sprintf (& Req->ReqBuffer [Len], "User-Agent: %s\r\nAccept: */*\r\n\r\n", VERSHTTP);
  1452.     Req->ReqRecv = strlen (Req->ReqBuffer);
  1453.     debug (D_QUEUE, ("initiating request '%s'\n", Req->ReqBuffer));
  1454.     ServConnect (Req);
  1455. }
  1456.  
  1457.  
  1458. /*)) */
  1459. /*(( "CheckTimeouts ()" */
  1460. /* Check whether any timeouts have occured */
  1461. /* it is checked at least every MIN_TIMEOUT to 2*MIN_TIMEOUT seconds.
  1462.  * Thus MIN_TIMEOUT is the minimal granularity for checks. */
  1463.  
  1464. void CheckTimeouts (void)
  1465. {
  1466.     int i;
  1467.     long CalcTime;
  1468.     request_t *Req;
  1469.     struct DateStamp TimeoutStamp;  /* Timeout check Stamp */
  1470.  
  1471.     DateStamp (&RequestStamp);
  1472.  
  1473.     /* Calculate NextCheck stamp */
  1474.     NextCheckStamp.ds_Minute        = NextCheckStamp.ds_Days   = 0;
  1475.     if ( (NextCheckStamp.ds_Tick    = RequestStamp.ds_Tick   + (MIN_TIMEOUT % 60) * TICKS_PER_SECOND) >= 60 * TICKS_PER_SECOND)
  1476.     {
  1477.     NextCheckStamp.ds_Tick     -= 60 * TICKS_PER_SECOND;
  1478.     NextCheckStamp.ds_Minute    = 1;
  1479.     }
  1480.     if ( (NextCheckStamp.ds_Minute += RequestStamp.ds_Minute + (MIN_TIMEOUT / 60) % (24 * 60)) >= 24 * 60)
  1481.     {
  1482.     NextCheckStamp.ds_Minute   -= 24 * 60;
  1483.     NextCheckStamp.ds_Days      = 1;
  1484.     }
  1485.     NextCheckStamp.ds_Days         += RequestStamp.ds_Days   + MIN_TIMEOUT / (24 * 60 * 60);
  1486.  
  1487.     /* Calculate Timeout check stamp */
  1488.     /* Could use the DateStamp entries, but I don't wanna rely on the entries being singed */
  1489.     CalcTime         = RequestStamp.ds_Tick - (TimeoutTime % 60) * TICKS_PER_SECOND;
  1490.     if (CalcTime < 0)
  1491.     {
  1492.     TimeoutStamp.ds_Tick = CalcTime + 60 * TICKS_PER_SECOND;
  1493.     CalcTime = -1;
  1494.     }
  1495.     else
  1496.     {
  1497.     TimeoutStamp.ds_Tick = CalcTime;
  1498.     CalcTime = 0;
  1499.     }
  1500.     CalcTime        += RequestStamp.ds_Minute - (TimeoutTime / 60) % (24 * 60);
  1501.     if (CalcTime < 0)
  1502.     {
  1503.     TimeoutStamp.ds_Minute = CalcTime + 24 * 60;
  1504.     CalcTime = -1;
  1505.     }
  1506.     else
  1507.     {
  1508.     TimeoutStamp.ds_Minute = CalcTime;
  1509.     CalcTime = 0;
  1510.     }
  1511.     CalcTime        += RequestStamp.ds_Days - TimeoutTime / (24 * 60 * 60);
  1512.     assert (CalcTime >= 0);                              /* kidding?!? */
  1513.     TimeoutStamp.ds_Days = CalcTime;
  1514.  
  1515.     /* Check requests */
  1516.     for (i=0, Req=Requests; i < MaxRequests; i++, Req++)
  1517.     if (Req->Flags)
  1518.     {
  1519.         if (CompareDates (&Req->LastStamp, &TimeoutStamp) > 0)
  1520.         {
  1521.         if (Req->Flags & REQ_CONNSOCKET)
  1522.         {
  1523.             ErrToReq (Req, 404, 0, "Timeout", "The specified host did not send any more data.<BR>\n"
  1524.                   "The connection was cancled.",
  1525.                   NULL);
  1526.             Req->LastStamp = RequestStamp;
  1527.             ErrToData (Req, TTimeout, KeepUnfinished);
  1528.             DeleteConnect (Req, KeepUnfinished);
  1529.         }
  1530.         else
  1531.         {
  1532.             LogErr  (Req, L_ERROR, NULL, 0, "Client timeout");
  1533.             DeleteRequest (Req, FALSE);
  1534.         }
  1535.         }
  1536.     }
  1537. }
  1538.  
  1539. /*)) */
  1540. /*(( "CheckGetQueued ()" */
  1541.  
  1542. /* Check number of pending requests and initiate getting of queued URLs */
  1543.  
  1544. void CheckGetQueued (int Force)         /* Force == 1 resets getqueueud state */
  1545. {
  1546.     static queue_t *q = NULL;
  1547.     cachefile_t Cache = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  1548.     static int State = 1;
  1549.  
  1550.     if (Force)
  1551.     State = 1;
  1552.     if (State == 3 || ! GetQueued || OffLine)
  1553.     return;
  1554.  
  1555.     debug (D_QUEUE, ("Queue state %d\n", State));
  1556.  
  1557.     while (RequestsFree > MinRequests)
  1558.     {
  1559.     switch (State) {
  1560.     case 1:                         /* get all not yet existing Urls first */
  1561.         if ( (q = QueueNext (q)) )
  1562.         {
  1563.         if (CacheGet (&Cache, q->Url, NULL) != 1)
  1564.         {
  1565.             RequestQueued (q->Url);
  1566.             QueueUnqueue (q);
  1567.         }
  1568.         break;
  1569.         }
  1570.         State = 2;
  1571.         /* No break! */
  1572.  
  1573.     case 2:
  1574.         if ( (q = QueueNext (q)) )
  1575.         {
  1576.         RequestQueued (q->Url);
  1577.         QueueUnqueue (q);
  1578.         break;
  1579.         }
  1580.         State = 3;
  1581.         return;
  1582.  
  1583.     default:
  1584.         assert (0);
  1585.     }
  1586.     }
  1587. }
  1588.  
  1589. /*)) */
  1590. /*(( "ShutdownConnects ()" */
  1591.  
  1592. /* Shutdown: Close and delete all invalid cache entries. */
  1593.  
  1594. void ShutdownConnects (void)
  1595. {
  1596.     request_t *Req;
  1597.     int i;
  1598.  
  1599.     /* queue nonready cache entries */
  1600.     for (Req = Requests; Req < & Requests [MaxRequests]; Req++)
  1601.     if (Req->Flags & REQ_NEWCACHE)
  1602.         /* Don't queue inaktive requests (the ones sent from the proxy) when CacheUnreadRequests=0.
  1603.          * Aktive requests are always requeued. */
  1604.         if (Req->Url [0] && ((Req->Flags & REQ_REQSOCKET) || CacheUnreadRequests))
  1605.         {
  1606.         LogErr  (Req, L_INFO, NULL, 0, "queueing URL on shutdown");
  1607.         QueueQueue (Req->Url);
  1608.         }
  1609.  
  1610.     /* now remove all connections */
  1611.     for (i=0, Req=Requests; i < MaxRequests; i++, Req++)
  1612.     if ((Req->Flags & REQ_CONNSOCKET))
  1613.     {
  1614.         LogErr  (Req, L_INFO, NULL, 0, "removing URL on shutdown");
  1615.         ErrToData (Req, TShutdown, KeepUnfinished);
  1616.         DeleteConnect (Req, KeepUnfinished);
  1617.     }
  1618. }
  1619.  
  1620.  
  1621. /*)) */
  1622. /*(( "main ()" */
  1623.  
  1624. /* The main routine */
  1625.  
  1626. void main (int argc, char **argv)
  1627. {
  1628.     netmethods_t *Netlist[] = { &NetAmiTCP, &NetAS225, NULL }; /* all available net protocolls */
  1629.     netmethods_t **TestNet;
  1630.     char   *ProxyHost = NULL;
  1631.     char   *LogFile   = "nil:";
  1632.     int    ProxyPort, NoHttpProxyProxy = FALSE;
  1633.     char   **FullArgv = argv;
  1634.  
  1635.     for (TestNet = Netlist; (Net = *TestNet); TestNet++)
  1636.     if (Net->init (FALSE))                 /* Check for open in nonblocking mode */
  1637.         break;
  1638.  
  1639.     if (! Net)
  1640.     {
  1641.     fprintf (stderr, "Couldn't open network protocol handler - perhaps the network stack is not running.\n"
  1642.              "Available network protocol handlers:\n");
  1643.     for (TestNet = Netlist; (Net = *TestNet); TestNet++)
  1644.         fprintf (stderr, "%s\n", Net->Descr);
  1645.     fprintf (stdout, TEST_NOTCPIP "\n");
  1646.     ExitAll (20);
  1647.     }
  1648.  
  1649.     PrgName = *argv++;
  1650.  
  1651.     if (--argc == 1)
  1652.     if (**argv == '?')
  1653.     {
  1654.         fprintf (stderr, "Usage: %s [proxy PROXYHOST PROXYPORT] [port PORT] [cache DIR] [del SECONDS]\n"
  1655.              "[expire SECONDS] [reload SECONDS] [timeout SECONDS] [log FILE]\n"
  1656.              "[numreq NUMBER] [minnumreq NUMBER] [unread] [offline] [get] [keepbad]\n"
  1657.              "[proxylocal] [noqueue] [nohttpproxyproxy] [debug LEVEL]\n\n"
  1658.              "Debug levels (may be or'ed):\n" DEBUG_LEVELS "\n"
  1659.              "The cache keyword will change the local directory.\n", PrgName);
  1660.         ExitAll (0);
  1661.     }
  1662.  
  1663.     while (argc)
  1664.     {
  1665.     if (strcasecmp (*argv, "proxy") == 0)
  1666.     {
  1667.         if (argc < 3)
  1668.         {
  1669.         fprintf (stderr, "%s: need two arguments for 'proxy'\n", PrgName);
  1670.         ExitAll (1);
  1671.         }
  1672.         argv++;
  1673.         argc -= 3;
  1674.         ProxyHost = *argv++;
  1675.         if (ProxyHost)
  1676.         ProxyProxy = HttpProxyProxy = TRUE;
  1677.  
  1678.         if ( (ProxyPort = atoi (*argv++)) <= 0)
  1679.         {
  1680.         fprintf (stderr, "%s: Wrong second argument for 'proxy' (need the port number).\n", PrgName);
  1681.         ExitAll (1);
  1682.         }
  1683.     }
  1684.     else if (strcasecmp (*argv, "port") == 0)
  1685.     {
  1686.         if (argc < 2)
  1687.         {
  1688.         fprintf (stderr, "%s: need a argument for 'port'\n", PrgName);
  1689.         ExitAll (1);
  1690.         }
  1691.         argv++;
  1692.         argc -= 2;
  1693.         ServerPort = atoi (*argv++);
  1694.     }
  1695.     else if (strcasecmp (*argv, "cache") == 0)
  1696.     {
  1697.         if (argc < 2)
  1698.         {
  1699.         fprintf (stderr, "%s: need a argument for 'cache'\n", PrgName);
  1700.         ExitAll (1);
  1701.         }
  1702.         argv++;
  1703.         argc -= 2;
  1704.         if (chdir (*argv++))
  1705.         {
  1706.         fprintf (stderr, "%s: no directory '%s': %s\n", PrgName, argv[-1], strerror (errno));
  1707.         ExitAll (1);
  1708.         }
  1709.     }
  1710.     else if (strcasecmp (*argv, "del") == 0)
  1711.     {
  1712.         if (argc < 2)
  1713.         {
  1714.         fprintf (stderr, "%s: need a argument for 'del'\n", PrgName);
  1715.         ExitAll (1);
  1716.         }
  1717.         argv++;
  1718.         argc -= 2;
  1719.         DelCacheTime = atoi (*argv++);
  1720.     }
  1721.     else if (strcasecmp (*argv, "expire") == 0)
  1722.     {
  1723.         if (argc < 2)
  1724.         {
  1725.         fprintf (stderr, "%s: need a argument for 'expire'\n", PrgName);
  1726.         ExitAll (1);
  1727.         }
  1728.         argv++;
  1729.         argc -= 2;
  1730.         ExpireCacheTime = atoi (*argv++);
  1731.     }
  1732.     else if (strcasecmp (*argv, "reload") == 0)
  1733.     {
  1734.         if (argc < 2)
  1735.         {
  1736.         fprintf (stderr, "%s: need a argument for 'reload'\n", PrgName);
  1737.         ExitAll (1);
  1738.         }
  1739.         argv++;
  1740.         argc -= 2;
  1741.         ReloadCacheTime = atoi (*argv++);
  1742.     }
  1743.     else if (strcasecmp (*argv, "timeout") == 0)
  1744.     {
  1745.         if (argc < 2)
  1746.         {
  1747.         fprintf (stderr, "%s: need a argument for 'timeout'\n", PrgName);
  1748.         ExitAll (1);
  1749.         }
  1750.         argv++;
  1751.         argc -= 2;
  1752.         TimeoutTime = atoi (*argv++);
  1753.     }
  1754.     else if (strcasecmp (*argv, "log") == 0)
  1755.     {
  1756.         if (argc < 2)
  1757.         {
  1758.         fprintf (stderr, "%s: need a argument for 'log'\n", PrgName);
  1759.         ExitAll (1);
  1760.         }
  1761.         argv++;
  1762.         argc -= 2;
  1763.         LogFile = *argv++;
  1764.     }
  1765.     else if (strcasecmp (*argv, "numreq") == 0)
  1766.     {
  1767.         if (argc < 2)
  1768.         {
  1769.         fprintf (stderr, "%s: need a argument for 'numreq'\n", PrgName);
  1770.         ExitAll (1);
  1771.         }
  1772.         argv++;
  1773.         argc -= 2;
  1774.         MaxRequests = atoi (*argv++);
  1775.     }
  1776.     else if (strcasecmp (*argv, "minnumreq") == 0)
  1777.     {
  1778.         if (argc < 2)
  1779.         {
  1780.         fprintf (stderr, "%s: need a argument for 'minnumreq'\n", PrgName);
  1781.         ExitAll (1);
  1782.         }
  1783.         argv++;
  1784.         argc -= 2;
  1785.         MinRequests = atoi (*argv++);
  1786.     }
  1787.     else if (strcasecmp (*argv, "unread") == 0)
  1788.     {
  1789.         argv++;
  1790.         argc--;
  1791.         CacheUnreadRequests = TRUE;
  1792.     }
  1793.     else if (strcasecmp (*argv, "keepbad") == 0)
  1794.     {
  1795.         argv++;
  1796.         argc--;
  1797.         KeepUnfinished = TRUE;
  1798.     }
  1799.     else if (strcasecmp (*argv, "offline") == 0)
  1800.     {
  1801.         argv++;
  1802.         argc--;
  1803.         OffLine = 1;
  1804.     }
  1805.     else if (strcasecmp (*argv, "get") == 0)
  1806.     {
  1807.         argv++;
  1808.         argc--;
  1809.         GetQueued = 1;
  1810.     }
  1811.     else if (strcasecmp (*argv, "proxylocal") == 0)
  1812.     {
  1813.         argv++;
  1814.         argc--;
  1815.         ProxyLocal = TRUE;
  1816.     }
  1817.     else if (strcasecmp (*argv, "noqueue") == 0)
  1818.     {
  1819.         argv++;
  1820.         argc--;
  1821.         QueueMode = FALSE;
  1822.     }
  1823.     else if (strcasecmp (*argv, "nohttpproxyproxy") == 0)
  1824.     {
  1825.         argv++;
  1826.         argc--;
  1827.         NoHttpProxyProxy = TRUE;
  1828.     }
  1829.     else if (strcasecmp (*argv, "debug") == 0)
  1830.     {
  1831.         if (argc < 2)
  1832.         {
  1833.         fprintf (stderr, "%s: need a argument for 'debug'\n", PrgName);
  1834.         ExitAll (1);
  1835.         }
  1836.         argv++;
  1837.         argc -= 2;
  1838. #ifdef DEBUG
  1839.         DebugLevel = atoi (*argv) | (1<<D_ALWAYS);      /* always output error debugs. */
  1840. #endif
  1841.         argv++;
  1842.     }
  1843.     else
  1844.     {
  1845.         fprintf (stderr, "%s: unknown option '%s'\n", PrgName, *argv);
  1846.         ExitAll (1);
  1847.     }
  1848.     }
  1849.  
  1850.     if (NoHttpProxyProxy)
  1851.     HttpProxyProxy = FALSE;
  1852.     if (MaxRequests <= MinRequests)
  1853.     {
  1854.     fprintf (stderr, "%s: argument 'numreq' %d needs nummeric value > 'minnumreq' %d\n", PrgName, MaxRequests, MinRequests);
  1855.     ExitAll (1);
  1856.     }
  1857.     if (MaxRequests * 2 >= Net->FDSize - 4)
  1858.     {
  1859.     fprintf (stderr, "%s: argument 'numreq' maximum value of %d exceeded\n", PrgName, (Net->FDSize - 5) / 2);
  1860.     ExitAll (1);
  1861.     }
  1862.     if (MinRequests < 0)
  1863.     {
  1864.     fprintf (stderr, "%s: argument 'minnumreq' must be positive or zero.\n", PrgName);
  1865.     ExitAll (1);
  1866.     }
  1867.  
  1868.     Init (ProxyHost, ProxyPort, LogFile, FullArgv);
  1869.  
  1870.     /* Never return */
  1871.     for (;;)
  1872.     {
  1873.     CheckGetQueued (FALSE);
  1874.     BuildFdSets ();
  1875.     debug (D_IO, ("Select... RequestsFree %d\n", RequestsFree));
  1876.     if (Net->select (ServServer, CheckTimeouts))
  1877.     {
  1878.         ShutdownConnects ();
  1879.         if (Net)              /* Cleaning up may take some time... */
  1880.         Net->exit ();
  1881.         Net = NULL;
  1882.         fprintf (stderr, "%s: terminating due to signal\n", PrgName);
  1883.         LogSpecial ("terminating due to signal\n");
  1884.         ExitAll (0);
  1885.     }
  1886.  
  1887.     DateStamp (&RequestStamp);
  1888.     if (CompareDates (&NextCheckStamp, &RequestStamp) > 0)
  1889.         CheckTimeouts ();
  1890.     ServRead ();
  1891.     ServWrite ();
  1892.     }
  1893. }
  1894. /*)) */
  1895.  
  1896.